﻿/*
Configuration Server Client library
 
Provides a single configuration point for the several instances (development, 
staging, testing, production, etc.) for web sites
 
Includes ideas and code from all over the internet. Specially, stacktrace, msdn, github.
 
Copyright (C) 2014 by Jaime Espinosa <jaimeespinosa60@gmail.com>
 
The MIT License (MIT)

Copyright (c) 2014 jaime espinosa

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

  @author Jaime Espinosa <jaimeespinosa60@gmail.com>
  @package configuracion
  @version 0.1
*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Runtime.Caching;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace configurationReader
{
    public static class configurationReader
    {
        public static int settingAsInt(string strKey = "")
        {
            string strSetting = setting(strKey);
            int intReturn = 0;
            if (int.TryParse(strSetting, out intReturn))
                return intReturn;
            else
                throw new Exception("configurationReader::settingAsInt:Invalid cast. Expected integer had :" + strSetting);
        }
        public static double settingAsDouble(string strKey = "")
        {
            string strSetting = setting(strKey);
            double dblReturn = 0.0;
            if (double.TryParse(strSetting, out dblReturn))
                return dblReturn;
            else
                throw new Exception("configurationReader::settingAsInt:Invalid cast. Expected double had :" + strSetting);
        }
        public static bool settingAsBool(string strKey = "")
        {
            string strSetting = setting(strKey);
            bool blnReturn = false;
            if (Boolean.TryParse(strSetting, out blnReturn))
                return blnReturn;
            else
                throw new Exception("configurationReader::settingAsInt:Invalid cast. Expected boolean had :" + strSetting);
        }
        public static string setting(string strKey = "")
        {
            MemoryCache Cache = MemoryCache.Default;

            if (Cache["configurationReader:" + strKey] == null)
            {
                // READS CONFIGURATION 
                configurationReaderSection config = (configurationReaderSection)System.Configuration.ConfigurationManager.GetSection("serverSettingReader/Settings");

                if ((config.Server == "") || (config.BundleName == "") || (config.User == "") || (config.Password == ""))
                {
                    throw new Exception("configurationReader::keyForBundleDirect:Invalid configuration");
                }

                if (strKey == "")
                    throw new Exception("configurationReader::keyForBundleDirect:Not key provided");

                try
                {
                    CacheItemPolicy policy = new CacheItemPolicy();
                    policy.AbsoluteExpiration = DateTime.Now.AddSeconds(config.cacheDuration);
                    policy.Priority = CacheItemPriority.Default;

                    // ADD TO CACHE
                    Cache.Set("configurationReader:" + strKey, doGet(strKey, config), policy);
                }
                catch (Exception ex)
                {
                    throw new Exception("configurationReader::keyForBundleDirect:" + infoExcepcion(ex));
                }
            }

            return Cache.Get("configurationReader:" + strKey).ToString();
        }
        private static string doGet(string strKey, configurationReaderSection config)
        {
            HttpWebRequest request = null;

            string strGetString = config.Server + "/consume/keyForBundle?strBundle={0}&strSet={1}&strKey={2}&strUser={3}&strPassword={4}";

            try
            {
                strGetString = string.Format(strGetString, config.BundleName, config.Set, strKey, config.User, config.Password);

                request = (HttpWebRequest)WebRequest.Create(strGetString);

                request.Method = "GET";
            }
            catch (Exception ex)
            {
                throw new Exception("configurationReader::doGet:" + ex.Message);
            }

            try
            {
                // allows self generated certificate to bu used
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);

                HttpWebResponse resp = (HttpWebResponse)request.GetResponse();

                string respStr = "";

                if (request.HaveResponse)
                {
                    if (resp.StatusCode == HttpStatusCode.OK || resp.StatusCode == HttpStatusCode.Accepted)
                    {
                        StreamReader respReader = new StreamReader(resp.GetResponseStream());
                        respStr = respReader.ReadToEnd(); // get the xml result in the string object  

                        var jsonResponse = new { error = "", OK = "" };
                        jsonResponse = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(respStr, jsonResponse);

                        if (jsonResponse.error != null)
                            throw new Exception(jsonResponse.error);
                        else
                            return jsonResponse.OK;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("configurationReader::doGet:" + ex.Message);
            }

            return "";
        }
        /// <summary>
        /// Allows the self generated ssl certificate to be used. Remove in production with "real" certificate
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="certificate"></param>
        /// <param name="chain"></param>
        /// <param name="sslPolicyErrors"></param>
        /// <returns></returns>
        private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }
        /// <summary>
        /// http://stackoverflow.com/questions/688336/show-line-number-in-exception-handling
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        public static string infoExcepcion(Exception ex)
        {
            //Get a StackTrace object for the exception
            StackTrace st = new StackTrace(ex, true);

            //Get the first stack frame
            StackFrame frame = st.GetFrame(0);

            //Get the file name
            string fileName = frame.GetFileName();

            //Get the method name
            string methodName = frame.GetMethod().Name;

            //Get the line number from the stack frame
            int line = frame.GetFileLineNumber();

            //Get the column number
            int col = frame.GetFileColumnNumber();

            return fileName + "-" + methodName + "-" + line + "-" + col + Environment.NewLine + "Exception:" + ex.Message;

        }
    }
}
